package com.sunyur.tailor.channel.common.inner.util;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: HuBaoding
 * @Description:
 * @Date: Created in 16:23 2021/8/30
 */
public class HttpDownloadUtil {

    private static final Logger LOGGER_INFO = LoggerFactory.getLogger(HttpDownloadUtil.class);

    /**
     * Http请求模板实例
     */
    private static final RestTemplate restTemplate;

    /**
     * 分片传输单位长度 10KB
     */
    private static final int RANGE_LENGTH = 512 * 1024;

    /**
     * 请求头信息
     */
    private static final String HEADER_ACCEPT_RANGES = "Accept-Ranges";

    /**
     * 请求头信息
     */
    private static final String HEADER_CONTENT_RANGE = "Content-Range";

    /**
     * 请求头信息
     */
    private static final String BYTES = "bytes";

    /**
     * 文件分片下载url
     */
    private static final String FILE_DOWNLOAD_API_URL = "/api/file/download/parts";

    static {
        SimpleClientHttpRequestFactory requestFactory = new  SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(600_000);
        requestFactory.setReadTimeout(600_000);
        restTemplate = new RestTemplate(requestFactory);
    }

    /**
     * 分片下载oss附件
     *
     * @param fileName  文件名称 -> (实例附件.txt)
     * @param ossKey    oss存储地址
     * @param tmpDir    文件临时存储目录地址 -> (/tmp/api/oss_file/file)
     * @return String   全路径下文件存储位置 -> (/tmp/api/oss_file/file/1610697983253.txt)
     * @author Ma_wei
     * @since 2021/1/15
     */
    public static String downloadOssFileOfRangePart(String baseUrl, Map<String,String> reqHeaders, String fileName, String ossKey, String tmpDir){
        if (StringUtils.isBlank(fileName) || StringUtils.isBlank(ossKey) || StringUtils.isBlank(tmpDir) || StringUtils.isBlank(baseUrl)) {
            return null;
        }
        try {
            int start = 0;
            // 是否结束
            boolean isOver = false;

            // 文件总长度
            long fileLength = 0L;
            long lastPos;
            // 文件名称
            String fileSuffix = fileName.substring(fileName.indexOf('.'));
            File file = new File(tmpDir, Joiner.on("").join(DateTime.now().getMillis(), RandomUtil.randomInt(1_000,9_999),fileSuffix));

            Map<String,String> paramMap = new HashMap<>(2);
            paramMap.put("name",fileName);
            paramMap.put("key",ossKey);

            try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file,true))) {
                do {
                    HttpEntity<String> httpEntity = buildPartsRangeEntity(fileLength,reqHeaders,start, JSON.toJSONString(paramMap));
                    LOGGER_INFO.info("JSONObject.toJSONString(paramMap) = {}",JSON.toJSONString(paramMap));
                    LOGGER_INFO.info("httpEntity = {}",httpEntity);
                    ResponseEntity<byte[]> exchange = restTemplate.exchange(baseUrl + FILE_DOWNLOAD_API_URL, HttpMethod.POST, httpEntity, byte[].class);

                    // 请求头信息
                    HttpHeaders headers = exchange.getHeaders();
                    LOGGER_INFO.info("headers = {}",JSON.toJSONString(headers));
                    List<String> acceptRanges = headers.get(HEADER_ACCEPT_RANGES);
                    if (Objects.isNull(acceptRanges)) {
                        return null;
                    } else {
                        String bytes = acceptRanges.get(0);
                        LOGGER_INFO.info("bytes = {}", bytes);
                        if (!BYTES.equals(bytes)) {
                            return null;
                        }
                    }
                    // bytes 0-999/15834
                    List<String> contentRanges = headers.get(HEADER_CONTENT_RANGE);
                    if (Objects.isNull(contentRanges)) {
                        return null;
                    } else {
                        String rangeBytes = contentRanges.get(0);
                        LOGGER_INFO.info("rangeBytes = {}", rangeBytes);
                        if (StringUtils.isBlank(rangeBytes)) {
                            return null;
                        }

                        String substring = rangeBytes.substring(rangeBytes.indexOf('-') + 1);
                        String[] split = substring.split("/");
                        if (split.length < 2) {
                            return null;
                        } else {
                            lastPos = Long.parseLong(split[0]);
                            if (fileLength == 0L) {
                                fileLength = Long.parseLong(split[1]);
                            }
                        }

                        if (lastPos + 1 >= fileLength) {
                            isOver = true;
                        }

                        byte[] body = exchange.getBody();
                        if (Objects.nonNull(body)) {
                            // 文件追加
                            bufferedOutputStream.write(body);
                        }
                    }
                    start += RANGE_LENGTH;
                } while (!isOver);
            }
            return tmpDir + File.separator + file.getName();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 组装分片请求entity
     *
     * @param start                 分片初始位置
     * @return HttpEntity<String>   HttpEntity
     * @author Ma_wei
     * @since 2021/1/15
     */
    private static HttpEntity<String> buildPartsRangeEntity(Long fileLength,Map<String,String> reqHeaders,int start,String param){
        String rangeValue = "bytes=";
        int endPos = start + RANGE_LENGTH - 1;
        if (fileLength != 0L && fileLength <= endPos ) {
            rangeValue += start + "-";
        } else {
            rangeValue += start + "-" + endPos;
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Range", rangeValue);
        headers.add("Content-Type","application/json;charset=UTF-8");
        reqHeaders.forEach(headers::add);
        return new HttpEntity<>(param,headers);
    }
}